package cn.bigfire.wedesk.net.protocol;

import cn.bigfire.wedesk.annotation.CMD;
import cn.bigfire.wedesk.bean.Wedesk;
import cn.hutool.core.lang.ClassScanner;
import cn.hutool.core.lang.Pair;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 协议号注册，兼容两种方式
 *
 * @Cmd(协议号) 优点:灵活,方便。   缺点:协议号不是统一管理
 * registerManual(协议号,Method)       优点:统一管理     缺点:需要手动注册协议号
 */
public class CmdMapper {

    private static final Map<Class<?>, Object> actionMapper = new HashMap<>();
    public static final Map<Integer, Pair<Method, Object>> methodMapper = new HashMap<>();

    static {
        try {
            //注解方法注册
            registerByPackage("cn.bigfire.wedesk.action");//扫包注册@Cmd注解
            //手动注册(写好方法后)
//            registerManual(Cmds.CMD_PING, SysAction.class.getDeclaredMethod("ping", ChannelHandlerContext.class, Channel.class, Mvc.Ping.class));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 手动注册协议号
     *
     * @param cmd    协议号
     * @param method 方法
     */
    public static void registerManual(int cmd, Method method) {
        try {
            method.setAccessible(true);//防止权限检查,优化反射性能开销
            Class<?> clazz = method.getDeclaringClass();
            if (!methodMapper.containsKey(cmd)) {
                Object actionObj = actionMapper.getOrDefault(clazz, clazz.newInstance());
                actionMapper.putIfAbsent(clazz, actionObj);
                methodMapper.put(cmd, new Pair<>(method, actionObj));
            } else {
                String classWhere = String.format("%s.%s(%s.java:1)", clazz.getName(), method.getName(), clazz.getSimpleName());
                System.err.printf("%sERROR,%s协议号重复.%n", classWhere, cmd);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 扫描指定包并注册包下所有的Action类
     * +
     *
     * @param packName 包名
     */
    public static void registerByPackage(String packName) {
        Set<Class<?>> classes = ClassScanner.scanPackage(packName);
        for (Class<?> aClass : classes) {
            registerByClass(aClass);
        }
    }

    /**
     * 注册Action类
     *
     * @param clazz action类Class
     */
    public static void registerByClass(Class<?> clazz) {
        try {
            if (!actionMapper.containsKey(clazz)) {//不存在时才注册，防止重复注册
                actionMapper.put(clazz, clazz.newInstance());
            }
            for (Method method : clazz.getDeclaredMethods()) {
                try {
                    method.setAccessible(true);
                    if (method.isAnnotationPresent(CMD.class)) {
                        CMD cmd = method.getAnnotation(CMD.class);
                        Wedesk.Cmd cmdEnum = cmd.value();
                        if (!methodMapper.containsKey(cmdEnum.getValue())) {
                            Object actionObj = actionMapper.getOrDefault(clazz, clazz.newInstance());
                            actionMapper.putIfAbsent(clazz, actionObj);
                            methodMapper.put(cmdEnum.getValue(), new Pair<>(method, actionObj));
                        } else {
                            String classWhere = String.format("%s.%s(%s.java:1)", clazz.getName(), method.getName(), clazz.getSimpleName());
                            System.err.printf("%sERROR,%s协议号重复.请检查协议号注册%n", classWhere, cmd.value());
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
